function hasCb(node, context) {
  if (node.type.indexOf("Function") !== -1) {
      const arr = context.getDeclaredVariables(node);
      return arr.some(function(v) {
          return v.defs[0].type === "Parameter" && v.name === "cb";
      });
  }
  return false;
}
function isCbCalled(info) {
  return info.cbCalled;
}

module.exports = {
  meta: {
    type: 'suggestion',
    schema: [],
  },
  create: function (context) {
    var funcInfoStack = [];
    var segmentInfoMap = Object.create(null);
    return {
      "onCodePathStart": function (codePath, node) {
        funcInfoStack.push({
          codePath: codePath,
          hasCb: hasCb(node, context),
        });
      },
      "onCodePathEnd": function (codePath, node) {
        var funcInfo = funcInfoStack[funcInfoStack.length - 1];
        funcInfoStack.pop()
        if (!funcInfo.hasCb) {
          return;
        }

        // check 'cb' was called in every path
        var cbCalled = codePath.finalSegments.every(function (segment) {
          var seg = segmentInfoMap[segment.id];
          return seg.cbCalled;
        })
        if (!cbCalled) {
          context.report({
            message: '`cb` should be called in every path.',
            node: node,
          })
        }
      },
      "onCodePathSegmentStart": function (segment) {
        var funcInfo = funcInfoStack[funcInfoStack.length - 1];
        if (!funcInfo.hasCb)
          return;
        var seg = segmentInfoMap[segment.id] = {
          cbCalled: false,
        };
        if (segment.prevSegments.length > 0) {
          var cbCalled = segment.prevSegments.every(isCbCalled);
          seg.cbCalled = cbCalled;
        }
      },
      "CallExpression": function(node) {
        var info = funcInfoStack[funcInfoStack.length - 1];
        if (!info.hasCb) {
          return;
        }
        if (node.callee.type === 'Identifier' && node.callee.name === 'cb') {
          info.codePath.currentSegments.forEach(function (segment) {
            var seg = segmentInfoMap[segment.id];
            seg.cbCalled = true;
          })
        }
      }
    };
  }  
}